
#include "CobLoader.h"
#include "ParserV1.h"
#include "3dMaterial.h"
#include "RgbTable.h"

int CCobLoader::LoadObject(	CObj4d&		obj,		// pointer to object
						   CVector4d*	pScale,		// initial scaling factors
						   CVector4d*	pPos,		// initial position
						   CVector4d*	pRot,		// initial rotations
						   int			vertex_flags)
{
	// this function loads a Caligari TrueSpace .COB file object in off disk, additionally
	// it allows the caller to scale, position, and rotate the object
	// to save extra calls later for non-dynamic objects, note that this function 
	// works with a OBJECT4DV1 which has no support for textures, or materials, etc, however we will
	// still parse them and get them ready for the next incarnation objects, so we can
	// re-use this code to support those features

	// create a parser object
	CPARSERV1 parser; 

	char seps[16];          // seperators for token scanning
	char token_buffer[256]; // used as working buffer for token
	char *token;            // pointer to next token

	int r,g,b;              // working colors

	// cache for texture vertices
	CVector2d texture_vertices[1024];

	int num_texture_vertices = 0;

	CMatrix44	mat_local,  // storage for local transform if user requests it in cob format
				mat_world;  // "   " for local to world " "

	// initialize matrices
	mat_local.Identify();
	mat_world.Identify();
	
	// Step 1: clear out the object and initialize it a bit
	obj.Reset();

	// set state of object to active and visible
	obj.state = OBJECT4DV1_STATE_ACTIVE | OBJECT4DV1_STATE_VISIBLE;

	// set position of object is caller requested position
	if (pPos)
	{
		// set position of object
		obj.world_pos = *pPos;
	} // end 

	// Step 2: open the file for reading using the parser
	if (!parser.Open((char*)m_strFile.c_str()))
	{
		//Write_Error("Couldn't open .COB file %s.", filename);
		return -1;
	} // end if

	// Step 3: 

	// lets find the name of the object first 
	while(1)
	{
		// get the next line, we are looking for "Name"
		if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
		{
			//Write_Error("Image 'name' not found in .COB file %s.", filename);
			return -1;
		} // end if

		// check for pattern?  
		if ( parser.Pattern_Match(parser.buffer, "['Name'] [s>0]") )
		{
			// name should be in second string variable, index 1
			strcpy(obj.name, parser.pstrings[1]);          
			//Write_Error("\nCOB Reader Object Name: %s", obj.name);

			break;    
		} // end if

	} // end while


	// step 4: get local and world transforms and store them

	// center 0 0 0
	// x axis 1 0 0
	// y axis 0 1 0
	// z axis 0 0 1

	while(1)
	{
		// get the next line, we are looking for "center"
		if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
		{
			//Write_Error("Center not found in .COB file %s.", filename);
			return -1;
		} // end if

		// check for pattern?  
		if ( parser.Pattern_Match(parser.buffer, "['center'] [f] [f] [f]") )
		{
			// the "center" holds the translation factors, so place in
			// last row of homogeneous matrix, note that these are row vectors
			// that we need to drop in each column of matrix
			mat_local.matrix[3][0] = -parser.pfloats[0]; // center x
			mat_local.matrix[3][1] = -parser.pfloats[1]; // center y
			mat_local.matrix[3][2] = -parser.pfloats[2]; // center z

			// ok now, the next 3 lines should be the x,y,z transform vectors
			// so build up   

			// "x axis" 
			parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
			parser.Pattern_Match(parser.buffer, "['x'] ['axis'] [f] [f] [f]");

			// place row in x column of transform matrix
			mat_local.matrix[0][0] = parser.pfloats[0]; // rxx
			mat_local.matrix[1][0] = parser.pfloats[1]; // rxy
			mat_local.matrix[2][0] = parser.pfloats[2]; // rxz

			// "y axis" 
			parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
			parser.Pattern_Match(parser.buffer, "['y'] ['axis'] [f] [f] [f]");

			// place row in y column of transform matrix
			mat_local.matrix[0][1] = parser.pfloats[0]; // ryx
			mat_local.matrix[1][1] = parser.pfloats[1]; // ryy
			mat_local.matrix[2][1] = parser.pfloats[2]; // ryz

			// "z axis" 
			parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
			parser.Pattern_Match(parser.buffer, "['z'] ['axis'] [f] [f] [f]");

			// place row in z column of transform matrix
			mat_local.matrix[0][2] = parser.pfloats[0]; // rzx
			mat_local.matrix[1][2] = parser.pfloats[1]; // rzy
			mat_local.matrix[2][2] = parser.pfloats[2]; // rzz

			//Print_Mat_4X4(&mat_local, "Local COB Matrix:");

			break;    
		} // end if

	} // end while

	// now "Transform"
	while(1)
	{
		// get the next line, we are looking for "Transform"
		if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
		{
			//Write_Error("Transform not found in .COB file %s.", filename);
			return -1;
		} // end if

		// check for pattern?  
		if ( parser.Pattern_Match(parser.buffer, "['Transform']") )
		{

			// "x axis" 
			parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
			parser.Pattern_Match(parser.buffer, "[f] [f] [f]");

			// place row in x column of transform matrix
			mat_world.matrix[0][0] = parser.pfloats[0]; // rxx
			mat_world.matrix[1][0] = parser.pfloats[1]; // rxy
			mat_world.matrix[2][0] = parser.pfloats[2]; // rxz

			// "y axis" 
			parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
			parser.Pattern_Match(parser.buffer, "[f] [f] [f]");

			// place row in y column of transform matrix
			mat_world.matrix[0][1] = parser.pfloats[0]; // ryx
			mat_world.matrix[1][1] = parser.pfloats[1]; // ryy
			mat_world.matrix[2][1] = parser.pfloats[2]; // ryz

			// "z axis" 
			parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
			parser.Pattern_Match(parser.buffer, "[f] [f] [f]");

			// place row in z column of transform matrix
			mat_world.matrix[0][2] = parser.pfloats[0]; // rzx
			mat_world.matrix[1][2] = parser.pfloats[1]; // rzy
			mat_world.matrix[2][2] = parser.pfloats[2]; // rzz

			//Print_Mat_4X4(&mat_world, "World COB Matrix:");

			// no need to read in last row, since it's always 0,0,0,1 and we don't use it anyway
			break;    

		} // end if

	} // end while

	// step 6: get number of vertices and polys in object
	while(1)
	{
		// get the next line, we are looking for "World Vertices" 
		if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
		{
			//Write_Error("'World Vertices' line not found in .COB file %s.", filename);
			return -1;
		} // end if

		// check for pattern?  
		if (parser.Pattern_Match(parser.buffer, "['World'] ['Vertices'] [i]") )
		{
			// simply extract the number of vertices from the pattern matching 
			// output arrays
			obj.num_vertices = parser.pints[0];

			//Write_Error("\nCOB Reader Num Vertices: %d", obj.num_vertices);
			break;    

		} // end if

	} // end while

	// Step 7: load the vertex list
	// now read in vertex list, format:
	// "d.d d.d d.d"
	for (int vertex = 0; vertex < obj.num_vertices; vertex++)
	{
		// hunt for vertex
		while(1)
		{
			// get the next vertex
			if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
			{
				//Write_Error("\nVertex list ended abruptly! in .COB file %s.", filename);
				return -1;
			} // end if

			// check for pattern?  
			if (parser.Pattern_Match(parser.buffer, "[f] [f] [f]"))
			{
				// at this point we have the x,y,z in the the pfloats array locations 0,1,2
				obj.vlist_local[vertex].vector[X] = parser.pfloats[0];
				obj.vlist_local[vertex].vector[Y] = parser.pfloats[1];
				obj.vlist_local[vertex].vector[Z] = parser.pfloats[2];
				obj.vlist_local[vertex].vector[W] = 1;

				// do vertex swapping right here, allow muliple swaps, why not!
				// defines for vertex re-ordering flags

				//#define VERTEX_FLAGS_INVERT_X   1    // inverts the Z-coordinates
				//#define VERTEX_FLAGS_INVERT_Y   2    // inverts the Z-coordinates
				//#define VERTEX_FLAGS_INVERT_Z   4    // inverts the Z-coordinates
				//#define VERTEX_FLAGS_SWAP_YZ    8    // transforms a RHS model to a LHS model
				//#define VERTEX_FLAGS_SWAP_XZ    16   // ???
				//#define VERTEX_FLAGS_SWAP_XY    32
				//#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64  // invert winding order from cw to ccw or ccw to cc
				//#define VERTEX_FLAGS_TRANSFORM_LOCAL         512   // if file format has local transform then do it!
				//#define VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD  1024  // if file format has local to world then do it!

				CVector4d temp_vector; // temp for calculations

				// now apply local and world transformations encoded in COB format
				if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL )
				{
					Mat_Mul_VECTOR4D_4X4(obj.vlist_local[vertex], mat_local, temp_vector);
					obj.vlist_local[vertex] = temp_vector; 
				} // end if 

				if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD )
				{
					Mat_Mul_VECTOR4D_4X4(obj.vlist_local[vertex], mat_world, temp_vector);
					obj.vlist_local[vertex] = temp_vector; 
				} // end if 

				float temp_f; // used for swapping

				// invert signs?
				if (vertex_flags & VERTEX_FLAGS_INVERT_X)
					obj.vlist_local[vertex].vector[X] =- obj.vlist_local[vertex].vector[X];

				if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
					obj.vlist_local[vertex].vector[Y] =- obj.vlist_local[vertex].vector[Y];

				if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
					obj.vlist_local[vertex].vector[Z] =- obj.vlist_local[vertex].vector[Z];

				// swap any axes?
				if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
					SWAP(obj.vlist_local[vertex].vector[Y], obj.vlist_local[vertex].vector[Z], temp_f);

				if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
					SWAP(obj.vlist_local[vertex].vector[X], obj.vlist_local[vertex].vector[Z], temp_f);

				if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
					SWAP(obj.vlist_local[vertex].vector[X], obj.vlist_local[vertex].vector[Y], temp_f);

				// scale vertices
				if (pScale)
				{
					obj.vlist_local[vertex].Scale(*pScale);
				} // end if

				//Write_Error("\nVertex %d = %f, %f, %f, %f", vertex,
				//	obj.vlist_local[vertex].x, 
				//	obj.vlist_local[vertex].y, 
				//	obj.vlist_local[vertex].z,
				//	obj.vlist_local[vertex].w);

				// found vertex, break out of while for next pass
				break;

			} // end if

		} // end while

	} // end for vertex

	// compute average and max radius
	obj.Calc_Radius();

	//Write_Error("\nObject average radius = %f, max radius = %f", 
	//	obj.avg_radius, obj.max_radius);


	// step 8: get number of texture vertices
	while(1)
	{
		// get the next line, we are looking for "Texture Vertices ddd" 
		if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
		{
			//Write_Error("'Texture Vertices' line not found in .COB file %s.", filename);
			return -1;
		} // end if

		// check for pattern?  
		if (parser.Pattern_Match(parser.buffer, "['Texture'] ['Vertices'] [i]") )
		{
			// simply extract the number of texture vertices from the pattern matching 
			// output arrays
			num_texture_vertices = parser.pints[0];

			//Write_Error("\nCOB Reader Texture Vertices: %d", num_texture_vertices);
			break;    

		} // end if

	} // end while

	// Step 9: load the texture vertex list in format "U V"
	// "d.d d.d"
	for (int tvertex = 0; tvertex < num_texture_vertices; tvertex++)
	{
		// hunt for texture
		while(1)
		{
			// get the next vertex
			if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
			{
				//Write_Error("\nTexture Vertex list ended abruptly! in .COB file %s.", filename);
				return -1;
			} // end if

			// check for pattern?  
			if (parser.Pattern_Match(parser.buffer, "[f] [f]"))
			{
				// at this point we have the U V in the the pfloats array locations 0,1 for this 
				// texture vertex, although we do nothing with them at this point with this parser
				texture_vertices[tvertex].vector[X] = parser.pfloats[0];
				texture_vertices[tvertex].vector[Y] = parser.pfloats[1];

				//Write_Error("\nTexture Vertex %d: U=%f, V=%f", tvertex,
				//	texture_vertices[tvertex].x, 
				//	texture_vertices[tvertex].y );

				// found vertex, break out of while for next pass
				break;

			} // end if

		} // end while

	} // end for

	int poly_material[OBJECT4DV1_MAX_POLYS]; // this holds the material index for each polygon
	// we need these indices since when reading the file
	// we read the polygons BEFORE the materials, so we need
	// this data, so we can go back later and extract the material
	// that each poly WAS assigned and get the colors out, since
	// objects and polygons do not currenlty support materials


	int material_index_referenced[MAX_MATERIALS];   // used to track if an index has been used yet as a material 
	// reference. since we don't know how many materials, we need
	// a way to count them up, but if we have seen a material reference
	// more than once then we don't increment the total number of materials
	// this array is for this

	// clear out reference array
	memset(material_index_referenced,0, sizeof(material_index_referenced));


	// step 10: load in the polygons
	// poly list starts off with:
	// "Faces ddd:"
	while(1)
	{
		// get next line
		if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
		{
			//Write_Error("\n'Faces' line not found in .ASC file %s.", filename);
			return -1;
		} // end if

		// check for pattern?  
		if (parser.Pattern_Match(parser.buffer, "['Faces'] [i]"))
		{
			//Write_Error("\nCOB Reader found face list in .COB file %s.", filename);

			// finally set number of polys
			obj.num_polys = parser.pints[0];

			break;
		} // end if
	} // end while

	// now read each face in format:
	// Face verts nn flags ff mat mm
	// the nn is the number of vertices, always 3
	// the ff is the flags, unused for now, has to do with holes
	// the mm is the material index number 


	int poly_surface_desc    = 0; // ASC surface descriptor/material in this case
	int poly_num_verts       = 0; // number of vertices for current poly (always 3)
	int num_materials_object = 0; // number of materials for this object

	for (int poly=0; poly < obj.num_polys; poly++)
	{
		// hunt until next face is found
		while(1)
		{
			// get the next polygon face
			if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
			{
				//Write_Error("\nface list ended abruptly! in .COB file %s.", filename);
				return -1;
			} // end if

			// check for pattern?  
			if (parser.Pattern_Match(parser.buffer, "['Face'] ['verts'] [i] ['flags'] [i] ['mat'] [i]"))
			{
				// at this point we have the number of vertices for the polygon, the flags, and it's material index
				// in the integer output array locations 0,1,2

				// store the material index for this polygon for retrieval later, but make sure adjust the 
				// the index to take into consideration that the data in parser.pints[2] is 0 based, and we need
				// an index relative to the entire library, so we simply need to add num_materials to offset the 
				// index properly, but we will leave this reference zero based for now... and fix up later
				poly_material[poly] = parser.pints[2];

				// update the reference array
				if (material_index_referenced[ poly_material[poly] ] == 0)
				{
					// mark as referenced
					material_index_referenced[ poly_material[poly] ] = 1;

					// increment total number of materials for this object
					num_materials_object++;
				} // end if        


				// test if number of vertices is 3
				if (parser.pints[0]!=3)
				{
					//Write_Error("\nface not a triangle! in .COB file %s.", filename);
					return -1;
				} // end if

				// now read out the vertex indices and texture indices format:
				// <vindex0, tindex0>  <vindex1, tindex1> <vindex1, tindex1> 
				if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
				{
					//Write_Error("\nface list ended abruptly! in .COB file %s.", filename);
					return -1;
				} // end if

				// lets replace ",<>" with ' ' to make extraction easy
				ReplaceChars(parser.buffer, parser.buffer, ",<>",' ');      
				parser.Pattern_Match(parser.buffer, "[i] [i] [i] [i] [i] [i]");

				// 0,2,4 holds vertex indices
				// 1,3,5 holds texture indices -- unused for now, no place to put them!

				// insert polygon, check for winding order invert
				if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
				{     
					poly_num_verts           = 3;
					obj.plist[poly].vert[0] = parser.pints[4];
					obj.plist[poly].vert[1] = parser.pints[2];
					obj.plist[poly].vert[2] = parser.pints[0];
				} // end if
				else
				{ // leave winding order alone
					poly_num_verts           = 3;
					obj.plist[poly].vert[0] = parser.pints[0];
					obj.plist[poly].vert[1] = parser.pints[2];
					obj.plist[poly].vert[2] = parser.pints[4];
				} // end else

				// point polygon vertex list to object's vertex list
				// note that this is redundant since the polylist is contained
				// within the object in this case and its up to the user to select
				// whether the local or transformed vertex list is used when building up
				// polygon geometry, might be a better idea to set to NULL in the context
				// of polygons that are part of an object
				obj.plist[poly].vlist = obj.vlist_local; 


				// set polygon to active
				obj.plist[poly].state = POLY4DV1_STATE_ACTIVE;    

				// found the face, break out of while for another pass
				break;

			} // end if

		} // end while      

		//Write_Error("\nPolygon %d:", poly);
		//Write_Error("\nLocal material Index=%d, total materials for object = %d, vert_indices [%d, %d, %d]", 
		//	poly_material[poly],
		//	num_materials_object,
		//	obj.plist[poly].vert[0],
		//	obj.plist[poly].vert[1],
		//	obj.plist[poly].vert[2]);       
	} // end for poly

	// now find materials!!! and we are out of here!
	int curr_material = 0;
	for (curr_material = 0; curr_material < num_materials_object; curr_material++)
	{
		// hunt for the material header "mat# ddd"
		while(1)
		{
			// get the next polygon material 
			if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
			{
				//Write_Error("\nmaterial list ended abruptly! in .COB file %s.", filename);
				return -1;
			} // end if

			// check for pattern?  
			if (parser.Pattern_Match(parser.buffer, "['mat#'] [i]") )
			{
				// extract the material that is being defined 
				int material_index = parser.pints[0];

				// get color of polygon, although it might be irrelevant for a textured surface
				while(1)
				{
					// get the next line
					if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
					{
						//Write_Error("\nRGB color ended abruptly! in .COB file %s.", filename);
						return -1;
					} // end if

					// replace the , comma's if there are any with spaces
					ReplaceChars(parser.buffer, parser.buffer, ",", ' ', 1);

					// look for "rgb float,float,float"
					if (parser.Pattern_Match(parser.buffer, "['rgb'] [f] [f] [f]") )
					{
						// extract data and store color in material libary
						// pfloats[] 0,1,2,3, has data
						materials[material_index + num_materials].color.r = (int)(parser.pfloats[0]*255 + 0.5);
						materials[material_index + num_materials].color.g = (int)(parser.pfloats[1]*255 + 0.5);
						materials[material_index + num_materials].color.b = (int)(parser.pfloats[2]*255 + 0.5);

						break; // while looking for rgb
					} // end if

				} // end while    

				// extract out lighting constants for the heck of it, they are on a line like this:
				// "alpha float ka float ks float exp float ior float"
				// alpha is transparency           0 - 1
				// ka is ambient coefficient       0 - 1
				// ks is specular coefficient      0 - 1
				// exp is highlight power exponent 0 - 1
				// ior is index of refraction (unused)

				// although our engine will have minimal support for these, we might as well get them
				while(1)
				{
					// get the next line
					if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
					{
						//Write_Error("\nmaterial properties ended abruptly! in .COB file %s.", filename);
						return -1;
					} // end if

					// look for "alpha float ka float ks float exp float ior float"
					if (parser.Pattern_Match(parser.buffer, "['alpha'] [f] ['ka'] [f] ['ks'] [f] ['exp'] [f]") )
					{
						// extract data and store in material libary
						// pfloats[] 0,1,2,3, has data
						materials[material_index + num_materials].color.a  = (UCHAR)(parser.pfloats[0]*255 + 0.5);
						materials[material_index + num_materials].ka       = parser.pfloats[1];
						materials[material_index + num_materials].kd       = 1; // hard code for now
						materials[material_index + num_materials].ks       = parser.pfloats[2];
						materials[material_index + num_materials].power    = parser.pfloats[3];

						// compute material reflectivities in pre-multiplied format to help engine
						for (int rgb_index=0; rgb_index < 3; rgb_index++)
						{
							// ambient reflectivity
							materials[material_index + num_materials].ra.rgba_M[rgb_index] = 
								( (UCHAR)(materials[material_index + num_materials].ka * 
								(float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );


							// diffuse reflectivity
							materials[material_index + num_materials].rd.rgba_M[rgb_index] = 
								( (UCHAR)(materials[material_index + num_materials].kd * 
								(float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );


							// specular reflectivity
							materials[material_index + num_materials].rs.rgba_M[rgb_index] = 
								( (UCHAR)(materials[material_index + num_materials].ks * 
								(float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );

						} // end for rgb_index

						break;
					} // end if

				} // end while    

				// now we need to know the shading model, it's a bit tricky, we need to look for the lines
				// "Shader class: color" first, then after this line is:
				// "Shader name: "xxxxxx" (xxxxxx) "
				// where the xxxxx part will be "plain color" and "plain" for colored polys 
				// or "texture map" and "caligari texture"  for textures
				// THEN based on that we hunt for "Shader class: reflectance" which is where the type
				// of shading is encoded, we look for the "Shader name: "xxxxxx" (xxxxxx) " again, 
				// and based on it's value we map it to our shading system as follows:
				// "constant" -> MATV1_ATTR_SHADE_MODE_CONSTANT 
				// "matte"    -> MATV1_ATTR_SHADE_MODE_FLAT
				// "plastic"  -> MATV1_ATTR_SHADE_MODE_GOURAUD
				// "phong"    -> MATV1_ATTR_SHADE_MODE_FASTPHONG 
				// and in the case that in the "color" class, we found a "texture map" then the "shading mode" is
				// "texture map" -> MATV1_ATTR_SHADE_MODE_TEXTURE 
				// which must be logically or'ed with the other previous modes

				//  look for the "shader class: color"
				while(1)
				{
					// get the next line
					if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
					{
						//Write_Error("\nshader class ended abruptly! in .COB file %s.", filename);
						return -1;
					} // end if

					if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['color']") )
					{
						break;
					} // end if

				} // end while

				// now look for the shader name for this class
				// Shader name: "plain color" or Shader name: "texture map"
				while(1)
				{
					// get the next line
					if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
					{
						//Write_Error("\nshader name ended abruptly! in .COB file %s.", filename);
						return -1;
					} // end if

					// replace the " with spaces
					ReplaceChars(parser.buffer, parser.buffer, "\"", ' ', 1);

					// is this a "plain color" poly?
					if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['plain'] ['color']") )
					{
						// not much to do this is default, we need to wait for the reflectance type
						// to tell us the shading mode

						break;
					} // end if

					// is this a "texture map" poly?
					if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['texture'] ['map']") )
					{
						// set the texture mapping flag in material
						SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_TEXTURE);

						// almost done, we need the file name of the darn texture map, its in this format:
						// file name: string "D:\Source\models\textures\wall01.bmp"

						// of course the filename in the quotes will change
						// so lets hunt until we find it...
						while(1)
						{
							// get the next line
							if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
							{
								//Write_Error("\ncouldnt find texture name! in .COB file %s.", filename);
								return -1;
							} // end if

							// replace the " with spaces
							ReplaceChars(parser.buffer, parser.buffer, "\"", ' ', 1);

							// is this the file name?
							if (parser.Pattern_Match(parser.buffer, "['file'] ['name:'] ['string'] [s>0]") )
							{
								// ok, simply convert to a real file name by changing the slashes
								ReplaceChars(parser.pstrings[3], parser.pstrings[3], "\\", '/',1);

								// and save the filename
								strcpy(materials[material_index + num_materials].texture_file, parser.pstrings[3]);

								break;
							} // end if

						} // end while

						break;
					} // end if

				} // end while 

				// alright, finally! Now we need to know what the actual shader type, now in the COB format
				// I have decided that in the "reflectance" class that's where we will look at what kind
				// of shader is supposed to be used on the polygon

				//  look for the "Shader class: reflectance"
				while(1)
				{
					// get the next line
					if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
					{
						//Write_Error("\nshader reflectance class not found in .COB file %s.", filename);
						return -1;
					} // end if

					// look for "Shader class: reflectance"
					if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['reflectance']") )
					{
						// now we know the next "shader name" is what we are looking for so, break

						break;
					} // end if

				} // end while    

				// looking for "Shader name: "xxxxxx" (xxxxxx) " again, 
				// and based on it's value we map it to our shading system as follows:
				// "constant" -> MATV1_ATTR_SHADE_MODE_CONSTANT 
				// "matte"    -> MATV1_ATTR_SHADE_MODE_FLAT
				// "plastic"  -> MATV1_ATTR_SHADE_MODE_GOURAUD
				// "phong"    -> MATV1_ATTR_SHADE_MODE_FASTPHONG 
				// and in the case that in the "color" class, we found a "texture map" then the "shading mode" is
				// "texture map" -> MATV1_ATTR_SHADE_MODE_TEXTURE 
				// which must be logically or'ed with the other previous modes
				while(1)
				{
					// get the next line
					if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
					{
						//Write_Error("\nshader name ended abruptly! in .COB file %s.", filename);
						return -1;
					} // end if

					// get rid of those quotes
					ReplaceChars(parser.buffer, parser.buffer, "\"",' ',1);

					// did we find the name?
					if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] [s>0]" ) )
					{
						// figure out which shader to use
						if (strcmp(parser.pstrings[2], "constant") == 0)
						{
							// set the shading mode flag in material
							SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_CONSTANT);
						} // end if
						else
							if (strcmp(parser.pstrings[2], "matte") == 0)
							{
								// set the shading mode flag in material
								SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FLAT);
							} // end if
							else
								if (strcmp(parser.pstrings[2], "plastic") == 0)
								{
									// set the shading mode flag in material
									SET_BIT(materials[curr_material + num_materials].attr, MATV1_ATTR_SHADE_MODE_GOURAUD);
								} // end if
								else
									if (strcmp(parser.pstrings[2], "phong") == 0)
									{
										// set the shading mode flag in material
										SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FASTPHONG);
									} // end if
									else
									{
										// set the shading mode flag in material
										SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FLAT);
									} // end else

									break;
					} // end if

				} // end while

				// found the material, break out of while for another pass
				break;

			} // end if found material

		} // end while looking for mat#1

	} // end for curr_material

	// at this point poly_material[] holds all the indices for the polygon materials (zero based, so they need fix up)
	// and we must access the materials array to fill in each polygon with the polygon color, etc.
	// now that we finally have the material libary loaded
	for (int curr_poly = 0; curr_poly < obj.num_polys; curr_poly++)
	{
		//Write_Error("\nfixing poly material %d from index %d to index %d", curr_poly, 
		//	poly_material[curr_poly],
		//	poly_material[curr_poly] + num_materials  );
		
		// fix up offset
		poly_material[curr_poly]  = poly_material[curr_poly] + num_materials;

		// we need to know what color depth we are dealing with, so check
		// the bits per pixel, this assumes that the system has already
		// made the call to DDraw_Init() or set the bit depth
		if (m_pDxDrawEnv->m_bpp == 16)
		{
			// cool, 16 bit mode
			SET_BIT(obj.plist[curr_poly].attr,POLY4DV1_ATTR_RGB16);
			obj.plist[curr_poly].color = RGB16Bit(materials[ poly_material[curr_poly] ].color.r, 
				materials[ poly_material[curr_poly] ].color.g, 
				materials[ poly_material[curr_poly] ].color.b);
			//Write_Error("\nPolygon 16-bit");
		} // end
		else
		{
			// 8 bit mode
			SET_BIT(obj.plist[curr_poly].attr,POLY4DV1_ATTR_8BITCOLOR);
			obj.plist[curr_poly].color = RgbTableInst::Inst()->RGBto8BitIndex(materials[ poly_material[curr_poly] ].color.r,
				materials[ poly_material[curr_poly] ].color.g,
				materials[ poly_material[curr_poly] ].color.b,
				m_pDxDrawEnv->m_palette, 0);

			//Write_Error("\nPolygon 8-bit, index=%d", obj.plist[curr_poly].color);
		} // end else

		// now set all the shading flags
		// figure out which shader to use
		if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_CONSTANT)
		{
			// set shading mode
			SET_BIT(obj.plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_CONSTANT);
		} // end if
		else
			if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_FLAT)
			{
				// set shading mode
				SET_BIT(obj.plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_FLAT);
			} // end if
			else
				if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_GOURAUD)
				{
					// set shading mode
					SET_BIT(obj.plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD);
				} // end if
				else
					if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_FASTPHONG)
					{
						// set shading mode
						SET_BIT(obj.plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_FASTPHONG);
					} // end if
					else
					{
						// set shading mode
						SET_BIT(obj.plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD);
					} // end if

					if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
					{
						// set shading mode
						SET_BIT(obj.plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_TEXTURE);
					} // end if

	} // end for curr_poly

	// local object materials have been added to database, update total materials in system
	num_materials+=num_materials_object;

#ifdef DEBUG_ON
	//for (curr_material = 0; curr_material < num_materials; curr_material++)
	//{
	//	Write_Error("\nMaterial %d", curr_material);

	//	Write_Error("\nint  state    = %d", materials[curr_material].state);
	//	Write_Error("\nint  id       = %d", materials[curr_material].id);
	//	Write_Error("\nchar name[64] = %s", materials[curr_material].name);
	//	Write_Error("\nint  attr     = %d", materials[curr_material].attr); 
	//	Write_Error("\nint r         = %d", materials[curr_material].color.r); 
	//	Write_Error("\nint g         = %d", materials[curr_material].color.g); 
	//	Write_Error("\nint b         = %d", materials[curr_material].color.b); 
	//	Write_Error("\nint alpha     = %d", materials[curr_material].color.a);
	//	Write_Error("\nint color     = %d", materials[curr_material].attr); 
	//	Write_Error("\nfloat ka      = %f", materials[curr_material].ka); 
	//	Write_Error("\nkd            = %f", materials[curr_material].kd); 
	//	Write_Error("\nks            = %f", materials[curr_material].ks); 
	//	Write_Error("\npower         = %f", materials[curr_material].power);
	//	Write_Error("\nchar texture_file = %s\n", materials[curr_material].texture_file);
	//} // end for curr_material
#endif

	// return success
	return 0;
}